package main

import (
	"fmt"
	"log"
	"strings"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var db *gorm.DB

func init() {
	// 数据库连接配置
	username := "root"
	password := "12345678"
	host := "localhost"
	port := "3306"
	dbname := "blog_system"

	// 构建DSN
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/?charset=utf8mb4&parseTime=True&loc=Local",
		username, password, host, port)

	var err error
	// 先连接到MySQL服务器
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info), // 显示SQL日志
	})
	if err != nil {
		log.Fatalf("连接数据库失败: %v", err)
	}

	// 创建数据库
	if err := createDatabase(dbname); err != nil {
		log.Fatalf("创建数据库失败: %v", err)
	}

	// 重新连接到指定数据库
	dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		username, password, host, port, dbname)

	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		log.Fatalf("连接到指定数据库失败: %v", err)
	}

	log.Println("数据库连接成功!")
}

// createDatabase 创建数据库
func createDatabase(dbname string) error {
	sql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci", dbname)
	return db.Exec(sql).Error
}

/* migrateModels 自动迁移模型 为啥自动迁移

数据库表结构 始终自动保持和 Go 结构体（模型）一致

 1. 
零 SQL 起步
 只要先定义结构体，一行  db.AutoMigrate(&Model{})  就能自动建表、建索引、建外键，项目启动时即可跑通。
2. 
随模型演进自动“补差”
 以后新增字段、改类型、加索引，再跑  AutoMigrate ，它会自动执行  ALTER TABLE ADD COLUMN/ADD INDEX  等，无需人工比对。
3. 
只增不减，保护数据
 GORM 默认不会删除列或索引，避免误删历史数据；只做“添加/修改”动作，生产环境更安全。
4. 
多环境一致
 开发、测试、生产都用同一套模型代码迁移，保证表结构 100% 一致，消除“我本地缺字段”这类问题。

  
配合版本控制
 模型文件在 Git 中，迁移历史可追溯；结合 CI/CD，部署即自动迁移，实现“代码即表结构”。
*/
func migrateModels() error {
	log.Println("开始自动迁移数据库模型...")

	// 自动迁移模型，创建表结构
	err := db.AutoMigrate(&User{}, &Post{}, &Comment{})
	if err != nil {
		return fmt.Errorf("自动迁移失败: %v", err)
	}

	log.Println("数据库模型迁移完成!")
	return nil
}

// createSampleData 创建示例数据
func createSampleData() error {
	log.Println("开始创建示例数据...")

	// 创建用户
	users := []User{
		{
			Username: "alice",
			Email:    "alice@example.com",
			Password: "hashed_password_1",
			Avatar:   "https://example.com/avatar1.jpg",
			Bio:      "我是Alice，喜欢写技术博客。",
		},
		{
			Username: "bob",
			Email:    "bob@example.com",
			Password: "hashed_password_2",
			Avatar:   "https://example.com/avatar2.jpg",
			Bio:      "我是Bob，热爱分享生活经验。",
		},
	}

	// 批量创建用户
	if err := db.Create(&users).Error; err != nil {
		return fmt.Errorf("创建用户失败: %v", err)
	}
	log.Printf("成功创建 %d 个用户", len(users))

	// 创建文章
	posts := []Post{
		{
			Title:     "Go语言入门指南",
			Content:   "这是一篇关于Go语言基础知识的详细介绍...",
			Summary:   "Go语言入门教程",
			Status:    "published",
			UserID:    users[0].ID,
			ViewCount: 100,
		},
		{
			Title:     "GORM使用技巧",
			Content:   "GORM是Go语言中非常流行的ORM框架...",
			Summary:   "GORM实战经验分享",
			Status:    "published",
			UserID:    users[0].ID,
			ViewCount: 85,
		},
		{
			Title:     "我的编程学习之路",
			Content:   "分享我从零基础到成为程序员的学习经历...",
			Summary:   "编程学习心得体会",
			Status:    "published",
			UserID:    users[1].ID,
			ViewCount: 156,
		},
	}

	// 批量创建文章
	if err := db.Create(&posts).Error; err != nil {
		return fmt.Errorf("创建文章失败: %v", err)
	}
	log.Printf("成功创建 %d 篇文章", len(posts))

	// 创建评论
	comments := []Comment{
		{
			Content: "这篇文章写得太好了！对新手很有帮助。",
			Status:  "active",
			PostID:  posts[0].ID,
			UserID:  users[1].ID,
		},
		{
			Content: "感谢分享，学到了很多。",
			Status:  "active",
			PostID:  posts[0].ID,
			UserID:  users[1].ID,
		},
		{
			Content: "GORM确实很强大，期待更多教程。",
			Status:  "active",
			PostID:  posts[1].ID,
			UserID:  users[1].ID,
		},
		{
			Content: "很励志的经历，给我很大鼓舞！",
			Status:  "active",
			PostID:  posts[2].ID,
			UserID:  users[0].ID,
		},
	}

	// 批量创建评论
	if err := db.Create(&comments).Error; err != nil {
		return fmt.Errorf("创建评论失败: %v", err)
	}
	log.Printf("成功创建 %d 条评论", len(comments))

	log.Println("示例数据创建完成!")
	return nil
}

// queryData 查询数据示例
func queryData() error {
	log.Println("\n=== 开始查询数据示例 ===")

	// 1. 查询所有用户及其文章
	var users []User
	if err := db.Preload("Posts").Find(&users).Error; err != nil {
		return fmt.Errorf("查询用户失败: %v", err)
	}

	fmt.Println("\n--- 用户及其文章 ---")
	for _, user := range users {
		fmt.Printf("用户: %s (%s)\n", user.Username, user.Email)
		fmt.Printf("  简介: %s\n", user.Bio)
		fmt.Printf("  文章数量: %d\n", len(user.Posts))
		for _, post := range user.Posts {
			fmt.Printf("    - %s (浏览量: %d)\n", post.Title, post.ViewCount)
		}
		fmt.Println()
	}

	// 2. 查询文章及其评论和作者
	var posts []Post
	if err := db.Preload("User").Preload("Comments").Preload("Comments.User").Find(&posts).Error; err != nil {
		return fmt.Errorf("查询文章失败: %v", err)
	}

	fmt.Println("--- 文章详情 ---")
	for _, post := range posts {
		fmt.Printf("文章: %s\n", post.Title)
		fmt.Printf("  作者: %s\n", post.User.Username)
		fmt.Printf("  摘要: %s\n", post.Summary)
		fmt.Printf("  状态: %s\n", post.Status)
		fmt.Printf("  浏览量: %d\n", post.ViewCount)
		fmt.Printf("  评论数: %d\n", len(post.Comments))

		for _, comment := range post.Comments {
			fmt.Printf("    评论 - %s: %s\n", comment.User.Username, comment.Content)
		}
		fmt.Println()
	}

	// 3. 统计查询
	var userCount, postCount, commentCount int64
	db.Model(&User{}).Count(&userCount)
	db.Model(&Post{}).Count(&postCount)
	db.Model(&Comment{}).Count(&commentCount)

	fmt.Println("--- 统计信息 ---")
	fmt.Printf("总用户数: %d\n", userCount)
	fmt.Printf("总文章数: %d\n", postCount)
	fmt.Printf("总评论数: %d\n", commentCount)

	return nil
}

/*
题目2：关联查询
基于上述博客系统的模型定义。
要求 ：
编写Go代码，使用Gorm查询某个用户发布的所有文章及其对应的评论信息。
编写Go代码，使用Gorm查询评论数量最多的文章信息。
*/

// advancedQueries 执行高级关联查询
func advancedQueries() error {
	log.Println("\n=== 开始执行题目2：关联查询 ===")

	// 1. 查询某个用户发布的所有文章及其对应的评论信息
	if err := queryUserPostsWithComments("alice"); err != nil {
		return fmt.Errorf("查询用户文章及评论失败: %v", err)
	}

	// 2. 查询评论数量最多的文章信息
	if err := queryPostWithMostComments(); err != nil {
		return fmt.Errorf("查询评论最多的文章失败: %v", err)
	}

	// 3. 额外查询：查询每篇文章的评论统计
	if err := queryPostCommentStats(); err != nil {
		return fmt.Errorf("查询文章评论统计失败: %v", err)
	}

	return nil
}

// queryUserPostsWithComments 查询某个用户发布的所有文章及其对应的评论信息
func queryUserPostsWithComments(username string) error {
	fmt.Printf("\n--- 查询用户 '%s' 发布的所有文章及评论 ---\n", username)

	var user User
	// 使用预加载查询用户及其文章和评论，同时预加载评论的用户信息
	err := db.Preload("Posts"). // 预加载文章
					Preload("Posts.Comments").      // 预加载文章的评论
					Preload("Posts.Comments.User"). // 预加载评论的用户信息
					Where("username = ?", username).
					First(&user).Error

	if err != nil {
		return fmt.Errorf("查询用户失败: %v", err)
	}

	fmt.Printf("用户信息：%s (%s)\n", user.Username, user.Email)
	fmt.Printf("发布文章数量：%d\n\n", len(user.Posts))

	// 遍历显示文章及评论详情
	for i, post := range user.Posts {
		fmt.Printf("第%d篇文章：\n", i+1)
		fmt.Printf("  标题：%s\n", post.Title)
		fmt.Printf("  摘要：%s\n", post.Summary)
		fmt.Printf("  状态：%s\n", post.Status)
		fmt.Printf("  浏览量：%d\n", post.ViewCount)
		fmt.Printf("  评论数量：%d\n", len(post.Comments))

		if len(post.Comments) > 0 {
			fmt.Println("  评论详情：")
			for j, comment := range post.Comments {
				fmt.Printf("    评论%d - %s：%s\n",
					j+1, comment.User.Username, comment.Content)
				fmt.Printf("           发表时间：%s\n",
					comment.CreatedAt.Format("2006-01-02 15:04:05"))
			}
		}
		fmt.Println()
	}

	return nil
}

// queryPostWithMostComments 查询评论数量最多的文章信息
func queryPostWithMostComments() error {
	fmt.Println("--- 查询评论数量最多的文章 ---")

	// 方法1：使用原生SQL子查询
	var postWithMaxComments struct {
		Post
		CommentCount int64 `json:"comment_count"`
	}

	err := db.Table("posts").
		Select("posts.*, COUNT(comments.id) as comment_count").
		Joins("LEFT JOIN comments ON posts.id = comments.post_id AND comments.deleted_at IS NULL").
		Where("posts.deleted_at IS NULL").
		Group("posts.id").
		Order("comment_count DESC").
		Limit(1).
		Scan(&postWithMaxComments).Error

	if err != nil {
		return fmt.Errorf("查询失败: %v", err)
	}

	// 获取完整的文章信息，包括作者和评论
	var fullPost Post
	err = db.Preload("User").
		Preload("Comments").
		Preload("Comments.User").
		Where("id = ?", postWithMaxComments.ID).
		First(&fullPost).Error

	if err != nil {
		return fmt.Errorf("获取完整文章信息失败: %v", err)
	}

	fmt.Printf("评论最多的文章：\n")
	fmt.Printf("  文章ID：%d\n", fullPost.ID)
	fmt.Printf("  标题：%s\n", fullPost.Title)
	fmt.Printf("  作者：%s\n", fullPost.User.Username)
	fmt.Printf("  摘要：%s\n", fullPost.Summary)
	fmt.Printf("  状态：%s\n", fullPost.Status)
	fmt.Printf("  浏览量：%d\n", fullPost.ViewCount)
	fmt.Printf("  评论数量：%d\n", postWithMaxComments.CommentCount)
	fmt.Printf("  创建时间：%s\n", fullPost.CreatedAt.Format("2006-01-02 15:04:05"))

	if len(fullPost.Comments) > 0 {
		fmt.Println("\n  所有评论：")
		for i, comment := range fullPost.Comments {
			fmt.Printf("    %d. %s：%s\n",
				i+1, comment.User.Username, comment.Content)
			fmt.Printf("       时间：%s\n",
				comment.CreatedAt.Format("2006-01-02 15:04:05"))
		}
	}

	return nil
}

// queryPostCommentStats 查询每篇文章的评论统计（额外功能）
func queryPostCommentStats() error {
	fmt.Println("\n--- 所有文章的评论统计 ---")

	type PostStats struct {
		PostID       uint   `json:"post_id"`
		Title        string `json:"title"`
		Author       string `json:"author"`
		CommentCount int64  `json:"comment_count"`
		ViewCount    int    `json:"view_count"`
	}

	var stats []PostStats
	err := db.Table("posts").
		Select(`posts.id as post_id, 
				posts.title, 
				users.username as author,
				COUNT(comments.id) as comment_count,
				posts.view_count`).
		Joins("LEFT JOIN users ON posts.user_id = users.id").
		Joins("LEFT JOIN comments ON posts.id = comments.post_id AND comments.deleted_at IS NULL").
		Where("posts.deleted_at IS NULL").
		Group("posts.id, posts.title, users.username, posts.view_count").
		Order("comment_count DESC, view_count DESC").
		Scan(&stats).Error

	if err != nil {
		return fmt.Errorf("查询文章统计失败: %v", err)
	}

	fmt.Printf("%-5s %-20s %-10s %-8s %-8s\n",
		"ID", "标题", "作者", "评论数", "浏览量")
	fmt.Println(strings.Repeat("-", 60))

	for _, stat := range stats {
		fmt.Printf("%-5d %-20s %-10s %-8d %-8d\n",
			stat.PostID,
			truncateString(stat.Title, 18),
			stat.Author,
			stat.CommentCount,
			stat.ViewCount)
	}

	return nil
}

// truncateString 截断字符串用于显示
func truncateString(s string, maxLen int) string {
	if len(s) <= maxLen {
		return s
	}
	return s[:maxLen-3] + "..."
}

/*
题目3：钩子函数测试
测试Post和Comment模型的钩子函数功能
*/

// testHookFunctions 测试钩子函数
func testHookFunctions() error {
	log.Println("\n=== 开始执行题目3：钩子函数测试 ===")

	// 1. 测试文章创建钩子函数
	if err := testPostCreateHook(); err != nil {
		return fmt.Errorf("测试文章创建钩子失败: %v", err)
	}

	// 2. 测试评论创建钩子函数
	if err := testCommentCreateHook(); err != nil {
		return fmt.Errorf("测试评论创建钩子失败: %v", err)
	}

	// 3. 测试评论删除钩子函数
	if err := testCommentDeleteHook(); err != nil {
		return fmt.Errorf("测试评论删除钩子失败: %v", err)
	}

	// 4. 测试文章删除钩子函数
	if err := testPostDeleteHook(); err != nil {
		return fmt.Errorf("测试文章删除钩子失败: %v", err)
	}

	return nil
}

// testPostCreateHook 测试文章创建钩子函数
func testPostCreateHook() error {
	fmt.Println("\n--- 测试文章创建钩子函数 ---")

	// 查询alice用户当前的文章数量
	var alice User
	err := db.Where("username = ?", "alice").First(&alice).Error
	if err != nil {
		return fmt.Errorf("查询用户失败: %v", err)
	}

	fmt.Printf("创建新文章前，用户 %s 的文章数量: %d\n", alice.Username, alice.PostCount)

	// 创建新文章，触发AfterCreate钩子
	newPost := Post{
		Title:   "测试钩子函数的文章",
		Content: "这是一篇用来测试钩子函数的文章内容...",
		Summary: "测试钩子函数",
		Status:  "published",
		UserID:  alice.ID,
	}

	err = db.Create(&newPost).Error
	if err != nil {
		return fmt.Errorf("创建文章失败: %v", err)
	}

	fmt.Printf("成功创建文章: %s (ID: %d)\n", newPost.Title, newPost.ID)

	// 重新查询用户信息，验证文章数量是否自动更新
	err = db.Where("id = ?", alice.ID).First(&alice).Error
	if err != nil {
		return fmt.Errorf("重新查询用户失败: %v", err)
	}

	fmt.Printf("创建新文章后，用户 %s 的文章数量: %d\n", alice.Username, alice.PostCount)

	// 查询新创建的文章状态
	var createdPost Post
	err = db.Where("id = ?", newPost.ID).First(&createdPost).Error
	if err != nil {
		return fmt.Errorf("查询新创建的文章失败: %v", err)
	}

	fmt.Printf("新文章的评论状态: %s, 评论数量: %d\n",
		createdPost.CommentStatus, createdPost.CommentCount)

	return nil
}

// testCommentCreateHook 测试评论创建钩子函数
func testCommentCreateHook() error {
	fmt.Println("\n--- 测试评论创建钩子函数 ---")

	// 找到刚创建的文章
	var testPost Post
	err := db.Where("title = ?", "测试钩子函数的文章").First(&testPost).Error
	if err != nil {
		return fmt.Errorf("查询测试文章失败: %v", err)
	}

	fmt.Printf("添加评论前，文章 '%s' 的评论状态: %s, 评论数量: %d\n",
		testPost.Title, testPost.CommentStatus, testPost.CommentCount)

	// 找到bob用户
	var bob User
	err = db.Where("username = ?", "bob").First(&bob).Error
	if err != nil {
		return fmt.Errorf("查询bob用户失败: %v", err)
	}

	// 创建评论，触发AfterCreate钩子
	newComment := Comment{
		Content: "这是对钩子函数测试文章的第一条评论！",
		Status:  "active",
		PostID:  testPost.ID,
		UserID:  bob.ID,
	}

	err = db.Create(&newComment).Error
	if err != nil {
		return fmt.Errorf("创建评论失败: %v", err)
	}

	fmt.Printf("成功创建评论: %s\n", newComment.Content)

	// 重新查询文章信息，验证评论状态是否自动更新
	err = db.Where("id = ?", testPost.ID).First(&testPost).Error
	if err != nil {
		return fmt.Errorf("重新查询文章失败: %v", err)
	}

	fmt.Printf("添加评论后，文章 '%s' 的评论状态: %s, 评论数量: %d\n",
		testPost.Title, testPost.CommentStatus, testPost.CommentCount)

	// 再添加一条评论
	secondComment := Comment{
		Content: "这是第二条评论！",
		Status:  "active",
		PostID:  testPost.ID,
		UserID:  bob.ID,
	}

	err = db.Create(&secondComment).Error
	if err != nil {
		return fmt.Errorf("创建第二条评论失败: %v", err)
	}

	// 再次查询文章状态
	err = db.Where("id = ?", testPost.ID).First(&testPost).Error
	if err != nil {
		return fmt.Errorf("再次查询文章失败: %v", err)
	}

	fmt.Printf("添加第二条评论后，文章 '%s' 的评论状态: %s, 评论数量: %d\n",
		testPost.Title, testPost.CommentStatus, testPost.CommentCount)

	return nil
}

// testCommentDeleteHook 测试评论删除钩子函数
func testCommentDeleteHook() error {
	fmt.Println("\n--- 测试评论删除钩子函数 ---")

	// 找到测试文章
	var testPost Post
	err := db.Where("title = ?", "测试钩子函数的文章").First(&testPost).Error
	if err != nil {
		return fmt.Errorf("查询测试文章失败: %v", err)
	}

	fmt.Printf("删除评论前，文章 '%s' 的评论状态: %s, 评论数量: %d\n",
		testPost.Title, testPost.CommentStatus, testPost.CommentCount)

	// 查找该文章的所有评论
	var comments []Comment
	err = db.Where("post_id = ?", testPost.ID).Find(&comments).Error
	if err != nil {
		return fmt.Errorf("查询评论失败: %v", err)
	}

	fmt.Printf("找到 %d 条评论\n", len(comments))

	// 删除第一条评论
	if len(comments) > 0 {
		err = db.Delete(&comments[0]).Error
		if err != nil {
			return fmt.Errorf("删除第一条评论失败: %v", err)
		}

		fmt.Printf("成功删除第一条评论\n")

		// 重新查询文章状态
		err = db.Where("id = ?", testPost.ID).First(&testPost).Error
		if err != nil {
			return fmt.Errorf("重新查询文章失败: %v", err)
		}

		fmt.Printf("删除第一条评论后，文章 '%s' 的评论状态: %s, 评论数量: %d\n",
			testPost.Title, testPost.CommentStatus, testPost.CommentCount)
	}

	// 删除剩余的评论
	if len(comments) > 1 {
		err = db.Delete(&comments[1]).Error
		if err != nil {
			return fmt.Errorf("删除第二条评论失败: %v", err)
		}

		fmt.Printf("成功删除第二条评论\n")

		// 再次查询文章状态
		err = db.Where("id = ?", testPost.ID).First(&testPost).Error
		if err != nil {
			return fmt.Errorf("再次查询文章失败: %v", err)
		}

		fmt.Printf("删除所有评论后，文章 '%s' 的评论状态: %s, 评论数量: %d\n",
			testPost.Title, testPost.CommentStatus, testPost.CommentCount)
	}

	return nil
}

// testPostDeleteHook 测试文章删除钩子函数
func testPostDeleteHook() error {
	fmt.Println("\n--- 测试文章删除钩子函数 ---")

	// 查询alice用户当前的文章数量
	var alice User
	err := db.Where("username = ?", "alice").First(&alice).Error
	if err != nil {
		return fmt.Errorf("查询用户失败: %v", err)
	}

	fmt.Printf("删除文章前，用户 %s 的文章数量: %d\n", alice.Username, alice.PostCount)

	// 找到测试文章
	var testPost Post
	err = db.Where("title = ?", "测试钩子函数的文章").First(&testPost).Error
	if err != nil {
		return fmt.Errorf("查询测试文章失败: %v", err)
	}

	// 删除测试文章，触发BeforeDelete钩子
	err = db.Delete(&testPost).Error
	if err != nil {
		return fmt.Errorf("删除文章失败: %v", err)
	}

	fmt.Printf("成功删除文章: %s\n", testPost.Title)

	// 重新查询用户信息，验证文章数量是否自动更新
	err = db.Where("id = ?", alice.ID).First(&alice).Error
	if err != nil {
		return fmt.Errorf("重新查询用户失败: %v", err)
	}

	fmt.Printf("删除文章后，用户 %s 的文章数量: %d\n", alice.Username, alice.PostCount)

	return nil
}

func main() {
	// 1. 自动迁移模型
	if err := migrateModels(); err != nil {
		log.Fatalf("模型迁移失败: %v", err)
	}

	// 2. 创建示例数据
	if err := createSampleData(); err != nil {
		log.Printf("创建示例数据失败: %v", err)
	}

	// 3. 查询数据示例
	if err := queryData(); err != nil {
		log.Printf("查询数据失败: %v", err)
	}

	// 4. 执行题目2的关联查询
	if err := advancedQueries(); err != nil {
		log.Printf("高级查询失败: %v", err)
	}

	// 5. 执行题目3的钩子函数测试
	if err := testHookFunctions(); err != nil {
		log.Printf("钩子函数测试失败: %v", err)
	}

	log.Println("\n程序执行完成!")
}
